﻿using System;
using System.Collections.Generic;
using System.Text;
using ET;
using Marx;

namespace Demo
{
    public enum ClientState
    {
        Normal,
        Destroying,
        Destroyed,
    }

    public class ClientAgent : Client
    {
        public override long PlayerId { get => playerId_; }
        public int LogicId => logicId_;
        public bool HasDestroyTimer => destroyTimer_ > 0;

        private int logicId_;
        private CoroutineLockComponent lock_;
        private ClientState state_;
        private int destroyTimer_;

        private long playerId_;
        private long actorId_;

        public ClientAgent(string account, long accountId) : base(account, accountId)
        {
            playerId_ = 0;
            actorId_ = 0;
            state_ = ClientState.Normal;
            lock_ = Game.Scene.GetComponent<CoroutineLockComponent>();
        }

        public async ETTask SetPlayer(long playerId, int logicId)
        {
            if (playerId_ == playerId)
            {
                return;
            }

            playerId_ = playerId;
            logicId_ = logicId;            
            actorId_ = await LocationProxyComponent.Instance.Get(playerId_);
        }

        public void SetPlayer(long playerId, long actorId, int logicId)
        {
            logicId_ = logicId;
            actorId_ = actorId;
            playerId_ = playerId;
        }

        public override async ETTask<IActorResponse> Call(IActorRequest req)
        {
            if (actorId_ == 0)
            {
                IActorResponse resp = new ActorResponse()
                {
                    Error = 1,
                    RpcId = req.RpcId,
                };
                return resp;
            }
            return await MessageHelper.CallActor(actorId_, req);
        }

        public override void Send(IMessage ntf)
        {
            if (actorId_ == 0)
            {
                Log.Error("ClientAgent send message fail, actorId == 0");
                return;
            }
            MessageHelper.SendActor(actorId_, ntf as IActorMessage);
        }

        public async ETTask DestroyPlayer()
        {
            try
            {
                if (playerId_ != 0)
                {
                    await MessageHelper.CallActor(actorId_, new SSDestroyPlayerRequest());
                }
                playerId_ = 0;
                actorId_ = 0;
            }
            catch(Exception e)
            {
                Log.Error("Destroy player fail");
                Log.Error(e);
            }
        }

        public override void Destroy()
        {
            if (state_ == ClientState.Destroying || state_ == ClientState.Destroyed)
            {
                return;
            }

            long destroyTime = ServerConfigration.Instance.Player.OfflineProtectTime;
            state_ = ClientState.Destroying;
            destroyTimer_ = GlobalTimer.Instance.CreateTimer(id => {
                DoDestroy().Coroutine();
            }, destroyTime);
            Log.Debug("Player[" + Account + "] will be destroyed after [" + destroyTime.ToString() + "] milliseconds");
        }

        public async ETTask DestroyImmedately()
        {
            CancelDestroy();

            state_ = ClientState.Destroying;
            await DoDestroy();
        }

        private async ETTask DoDestroy()
        {
            try
            {
                using (await lock_.Wait(MarxCoroutineLock.Account, AccountId))
                {
                    if (state_ != ClientState.Destroying)
                    {
                        return;
                    }

                    if (Session != null && !Session.IsDisposed)
                    {
                        Session.Dispose();
                        Session = null;
                    }

                    state_ = ClientState.Destroyed;
                    ClientManager.Instance.Remove(AccountId);

                    await DestroyPlayer();
                }
            }
            catch (Exception e)
            {
                Log.Error(e);
            }
        }

        public void CancelDestroy()
        {
            state_ = ClientState.Normal;
            if (HasDestroyTimer)
            {
                GlobalTimer.Instance.RemoveTimer(destroyTimer_);
                destroyTimer_ = 0;
            }
        }
    }

}
